Skip to content

Conversation

@ethangraham2001
Copy link
Owner

@ethangraham2001 ethangraham2001 commented Jan 6, 2026

Cover letter:

From 5c3e1554debe3fd40035076c7bbecef2efa1b50d Mon Sep 17 00:00:00 2001
From: Ethan Graham <ethan.w.s.graham@gmail.com>
Date: Sun, 11 Jan 2026 18:48:03 +0100
Subject: [PATCH v4 0/6] KFuzzTest: a new kernel fuzzing framework 

This patch series introduces KFuzzTest, a lightweight framework for
creating in-kernel fuzz targets for internal kernel functions.

The primary motivation for KFuzzTest is to simplify the fuzzing of
low-level, relatively stateless functions (e.g., data parsers, format
converters) that are difficult to exercise effectively from the syscall
boundary. It is intended for in-situ fuzzing of kernel code without
requiring that it be built as a separate userspace library or that its
dependencies be stubbed out.

Following feedback from the Linux Plumbers Conference and mailing list
discussions, this version of the framework has been significantly
simplified. It now focuses exclusively on handling raw binary inputs,
removing the complexity of the custom serialization format and DWARF
parsing found in previous iterations.

The core design consists of two main parts:
1. The `FUZZ_TEST_SIMPLE(name)` macro, which allows developers to define
   a fuzz test that accepts a buffer and its length.
2. A simplified debugfs interface that allows userspace fuzzers (or
   simple command-line tools) to pass raw binary blobs directly to the
   target function.

To validate the framework's end-to-end effectiveness, we performed an
experiment by manually introducing an off-by-one buffer over-read into
pkcs7_parse_message, like so:

- ret = asn1_ber_decoder(&pkcs7_decoder, ctx, data, datalen);
+ ret = asn1_ber_decoder(&pkcs7_decoder, ctx, data, datalen + 1);

A syzkaller instance fuzzing the new test_pkcs7_parse_message target
introduced in patch 7 successfully triggered the bug inside of
asn1_ber_decoder in under 30 seconds from a cold start. Similar
experiments on the other new fuzz targets (patches 8-9) also
successfully identified injected bugs, proving that KFuzzTest is
effective when paired with a coverage-guided fuzzing engine.

This patch series is structured as follows:
- Patch 1 introduces the core KFuzzTest API, including the main
  FUZZ_TEST_SIMPLE macro.
- Patch 2 adds the runtime implementation for the framework
- Patch 3 adds documentation.
- Patch 4 provides sample fuzz targets.
- Patch 5 defines fuzz targets for several functions in crypto/.
- Patch 6 adds maintainer information for KFuzzTest.

Changes since PR v3:
- Major simplification of the architecture, removing the complex
  `FUZZ_TEST` macro, the custom serialization format, domain
  constraints, annotations, and associated DWARF metadata regions.
- The framework now only supports `FUZZ_TEST_SIMPLE` targets, which
  accept raw binary data.
- Removed the userspace bridge tool as it is no longer required for
  serializing inputs.
- Updated documentation and samples to reflect the "simple-only"
  approach.


Ethan Graham (6):
  kfuzztest: add user-facing API and data structures
  kfuzztest: implement core module and input processing
  kfuzztest: add ReST documentation
  kfuzztest: add KFuzzTest sample fuzz targets
  crypto: implement KFuzzTest targets for PKCS7 and RSA parsing
  MAINTAINERS: add maintainer information for KFuzzTest

 Documentation/dev-tools/index.rst             |   1 +
 Documentation/dev-tools/kfuzztest.rst         | 152 ++++++++++++++++++
 MAINTAINERS                                   |   7 +
 crypto/asymmetric_keys/Makefile               |   2 +
 crypto/asymmetric_keys/tests/Makefile         |   4 +
 crypto/asymmetric_keys/tests/pkcs7_kfuzz.c    |  18 +++
 .../asymmetric_keys/tests/rsa_helper_kfuzz.c  |  22 +++
 include/asm-generic/vmlinux.lds.h             |  14 +-
 include/linux/kfuzztest.h                     |  90 +++++++++++
 lib/Kconfig.debug                             |   1 +
 lib/Makefile                                  |   2 +
 lib/kfuzztest/Kconfig                         |  20 +++
 lib/kfuzztest/Makefile                        |   4 +
 lib/kfuzztest/input.c                         |  42 +++++
 lib/kfuzztest/main.c                          | 150 +++++++++++++++++
 samples/Kconfig                               |   7 +
 samples/Makefile                              |   1 +
 samples/kfuzztest/Makefile                    |   3 +
 samples/kfuzztest/underflow_on_buffer.c       |  48 ++++++
 19 files changed, 587 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/dev-tools/kfuzztest.rst
 create mode 100644 crypto/asymmetric_keys/tests/Makefile
 create mode 100644 crypto/asymmetric_keys/tests/pkcs7_kfuzz.c
 create mode 100644 crypto/asymmetric_keys/tests/rsa_helper_kfuzz.c
 create mode 100644 include/linux/kfuzztest.h
 create mode 100644 lib/kfuzztest/Kconfig
 create mode 100644 lib/kfuzztest/Makefile
 create mode 100644 lib/kfuzztest/input.c
 create mode 100644 lib/kfuzztest/main.c
 create mode 100644 samples/kfuzztest/Makefile
 create mode 100644 samples/kfuzztest/underflow_on_buffer.c

@ethangraham2001 ethangraham2001 force-pushed the kfuzztest_pr_v4 branch 8 times, most recently from becb4fc to 4d04156 Compare January 11, 2026 20:14
* subdirectory under "/sys/kernel/debug/kfuzztest/<test-name>" containing one
* write-only "input" and optional "input_simple" files used for receiving
* inputs from userspace.
* Furthermore, a directory "/sys/kernel/debug/kfuzztest/_config" is created,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest using a consistent column limit between the code and the comments.
Right now the code seems to be allowing 100 columns, whereas the comments are limited to 80.

@ethangraham2001 ethangraham2001 force-pushed the kfuzztest_pr_v4 branch 2 times, most recently from 73934f7 to 5616eaf Compare January 12, 2026 18:58
Add the foundational user-facing components for the KFuzzTest framework.
This includes the main API header <linux/kfuzztest.h>, the Kconfig
option to enable the feature, and the required linker script changes
which introduce a new ELF section in vmlinux.

Note that KFuzzTest is intended strictly for debug builds only, and
should never be enabled in a production build. The fact that it exposes
internal kernel functions and state directly to userspace may constitute
a serious security vulnerability if used for any reason other than
testing.

The header defines:
- The FUZZ_TEST_SIMPLE() macro for creating test targets.
- The `struct kfuzztest_simple_target` structure used to register tests.
- The linker section (.kfuzztest_simple_target) where test metadata is
  stored for discovery by the framework.

This patch only adds the public interface and build integration; no
runtime logic is included.

Signed-off-by: Ethan Graham <ethan.w.s.graham@gmail.com>

---
PR v4:
- Remove the complex FUZZ_TEST macro and associated dependencies,
  including domain constraints, annotations, and de-serialization,
  dramatically simplifying the flow.
- Drop unused ELF sections (.kfuzztest_constraint, etc...) from the
  linker script, keeping only .kfuzztest_simple_target.
PR v3:
- Reorder definitions in kfuzztest.h for better flow and readability.
- Introduce __KFUZZTEST_CONSTRAINT macro in preparation for the
  introduction of the FUZZ_TEST_SIMPLE macro in the following patch,
  which uses it for manually emitting constraint metadata.
PR v1:
- Move KFuzzTest metadata definitions to generic vmlinux linkage so that
  the framework isn't bound to x86_64.
- Return -EFAULT when simple_write_to_buffer returns a value not equal
  to the input length in the main FUZZ_TEST macro.
- Enforce a maximum input size of 64KiB in the main FUZZ_TEST macro,
  returning -EINVAL when it isn't respected.
- Refactor KFUZZTEST_ANNOTATION_* macros.
- Taint the kernel with TAINT_TEST inside the FUZZ_TEST macro when a
  fuzz target is invoked for the first time.
---
Add the core runtime implementation for KFuzzTest. This includes the
module initialization, and the logic for receiving and processing
user-provided inputs through debugfs.

On module load, the framework discovers all of the simple test targets
(FUZZ_TEST_SIMPLE) by iterating over the .kfuzztest_simple_target
section, creating a corresponding debugfs directory with a write-only
'input_simple' file for each of them.

Writing to an 'input_simple' file triggers the following fuzzing
sequence:
1. The binary input is allocated and copied from userspace into a
   kernel buffer.
2. The buffer and its length are passed immediately to the user-defined
   test logic.
3. The kernel is tainted with TAINT_TEST to indicate that untrusted input
   has been fed directly to the internal kernel functions.

This lightweight implementation relies on the caller (e.g., a fuzzer or
script) to provide raw binary data that the target function can process.

Signed-off-by: Ethan Graham <ethan.w.s.graham@gmail.com>

---
PR v4:
- Remove parsing, relocation, and KASAN poisoning logic to support the
  move to a simple-only design.
- Remove the '_config' debugfs directory and associated state tracking
  (minimum alignment, invocation counts) to reduce complexity.
- Enforce zero offset in `kfuzztest_write_cb_common` to ensure inputs
  are passed down as single, contiguous blocks.
PR v3:
- Handle FUZZ_TEST_SIMPLE targets by creating a write-only
  'input_simple' under the fuzz target's directory.
- Add implementation for `kfuzztest_write_input_cb`.
PR v2:
- Fix build issues identified by the kernel test robot <lkp@intel.com>.
- Address some nits pointed out by Alexander Potapenko.
PR v1:
- Update kfuzztest/parse.c interfaces to take `unsigned char *` instead
  of `void *`, reducing the number of pointer casts.
- Expose minimum region alignment via a new debugfs file.
- Expose number of successful invocations via a new debugfs file.
- Refactor module init function, add _config directory with entries
  containing KFuzzTest state information.
- Account for kasan_poison_range() return value in input parsing logic.
- Validate alignment of payload end.
- Move static sizeof assertions into /lib/kfuzztest/main.c.
- Remove the taint in kfuzztest/main.c. We instead taint the kernel as
  soon as a fuzz test is invoked for the first time, which is done in
  the primary FUZZ_TEST macro.
RFC v2:
- The module's init function now taints the kernel with TAINT_TEST.
---
Add Documentation/dev-tools/kfuzztest.rst and reference it in the
dev-tools index.

Signed-off-by: Ethan Graham <ethan.w.s.graham@gmail.com>

---
PR v4:
- Rework documentation to focus exclusively on the `FUZZ_TEST_SIMPLE`
  macro, removing all references to the legacy complex targets and
  serialization format.
- Remove obsolete sections describing DWARF constraints, annotations,
  and the userspace bridge tool.
- Add examples demonstrating basic usage with standard command-line
  tools.
---
Add two simple fuzz target samples to demonstrate the KFuzzTest API and
provide basic self-tests for the framework.

These examples showcase how a developer can define a fuzz target using
the FUZZ_TEST_SIMPLE() macro. It also serves as a runtime sanity check,
ensuring that the framework correctly passes the input buffer and that
KASAN correctly detects out-of-bounds memory accesses (in this case, a
buffer underflow) on the allocated test data.

This target can be fuzzed naively by writing random data into the
debugfs 'input_simple' file and verifying that the KASAN report is
triggered.

Signed-off-by: Ethan Graham <ethan.w.s.graham@gmail.com>
Acked-by: Alexander Potapenko <glider@google.com>

---
PR v4:
- Remove the `test_underflow_on_nested_buffer` sample target which
  relied on the now removed `FUZZ_TEST` macro.
- Update the sample comment to demonstrate naive fuzzing (using `head`)
  instead of the removed bridge tool.
- Fix stale comments referencing internal layout structures.
PR v3:
- Use the FUZZ_TEST_SIMPLE macro in the `underflow_on_buffer` sample
  fuzz target instead of FUZZ_TEST.
PR v2:
- Fix build issues pointed out by the kernel test robot <lkp@intel.com>.
---
Add KFuzzTest targets for pkcs7_parse_message, rsa_parse_pub_key, and
rsa_parse_priv_key to serve as real-world examples of how the framework
is used.

These functions are ideal candidates for KFuzzTest as they perform
complex parsing of user-controlled data but are not directly exposed at
the syscall boundary. This makes them difficult to exercise with
traditional fuzzing tools and showcases the primary strength of the
KFuzzTest framework: providing an interface to fuzz internal functions.

To validate the effectiveness of the framework on these new targets, we
injected two artificial bugs and let syzkaller fuzz the targets in an
attempt to catch them.

The first of these was calling the asn1 decoder with an incorrect input
from pkcs7_parse_message, like so:

- ret = asn1_ber_decoder(&pkcs7_decoder, ctx, data, datalen);
+ ret = asn1_ber_decoder(&pkcs7_decoder, ctx, data, datalen + 1);

The second was bug deeper inside of asn1_ber_decoder itself, like so:

- for (len = 0; n > 0; n--)
+ for (len = 0; n >= 0; n--)

syzkaller was able to trigger these bugs, and the associated KASAN
slab-out-of-bounds reports, within seconds.

The targets are defined within crypto/asymmetric-keys/tests.

Signed-off-by: Ethan Graham <ethan.w.s.graham@gmail.com>

---
PR v4:
- Use pkcs7_free_message() instead of kfree() on the success path of the
  pkcs7_parse_message fuzz target.
- Dropped Ignat Korchagin's reviewed-by due to the functional change in
  switching from kfree to pkcs7_free_message.
- Restrict introduced fuzz targets to build only when their dependencies
  (CONFIG_PKCS7_MESSAGE_PARSER and CONFIG_CRYPTO_RSA) are built-in. This
  prevents linker errors when they are configured as modules, as
  KFuzzTest symbols are not exported.
PR v3:
- Use the FUZZ_TEST_SIMPLE macro for all introduced fuzz targets as
  they each take `(data, datalen)` pairs. This also removes the need for
  explicit constraints and annotations which become implicit.
PR v2:
- Make fuzz targets also depend on the KConfig options needed for the
  functions they are fuzzing, CONFIG_PKCS7_MESSAGE_PARSER and
  CONFIG_CRYPTO_RSA respectively.
- Fix build issues pointed out by the kernel test robot <lkp@intel.com>.
- Account for return value of pkcs7_parse_message, and free resources if
  the function call succeeds.
PR v1:
- Change the fuzz target build to depend on CONFIG_KFUZZTEST=y,
  eliminating the need for a separate config option for each individual
  file as suggested by Ignat Korchagin.
- Remove KFUZZTEST_EXPECT_LE on the length of the `key` field inside of
  the fuzz targets. A maximum length is now set inside of the core input
  parsing logic.
RFC v2:
- Move KFuzzTest targets outside of the source files into dedicated
  _kfuzz.c files under /crypto/asymmetric_keys/tests/ as suggested by
  Ignat Korchagin and Eric Biggers.
---
Add myself as maintainer and Alexander Potapenko as reviewer for
KFuzzTest.

Signed-off-by: Ethan Graham <ethan.w.s.graham@gmail.com>
Acked-by: Alexander Potapenko <glider@google.com>

---
PR v4:
- Remove reference to the kfuzztest-bridge tool that has been removed
PR v3:
- Update MAINTAINERS to reflect the correct location of kfuzztest-bridge
  under tools/testing as pointed out by SeongJae Park.
---
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants